﻿

//* Write a program that calculates the value of given arithmetical expression. The expression can contain the following elements only:
//Real numbers, e.g. 5, 18.33, 3.14159, 12.6
//Arithmetic operators: +, -, *, / (standard priorities)
//Mathematical functions: ln(x), sqrt(x), pow(x,y)
//Brackets (for changing the default priorities)
//    Examples:
//    (3+5.3) * 2.7 - ln(22) / pow(2.2, -1.7)  ~ 10.6
//    pow(2, 3.14) * (3 - (3 * sqrt(2) - 3.2) + 1.5*0.3)  ~ 21.22
//    Hint: Use the classical "shunting yard" algorithm and "reverse Polish notation".


using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;

class ReversePolishNotation
{
    static Dictionary<string, int?> precedence = new Dictionary<string, int?>()
    { 
        { "ln", 4 },
        { "sqrt", 4 },
        { "pow", 4 },
        { "*", 3 },
        { "/", 3 },
        { "-", 2 },
        { "+", 1 },
        { "(", null },
        { ")", null },
    };

    static string[] functions = { "ln", "sqrt", "pow" };

    static void Main()
    {
        Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;

        string input1 = "(3 + 5.3) * 2.7 - ln(22) / pow(2.2, -1.7)";
        string input2 = "pow(2, 3.14) * (3 - (3 * sqrt(2) - 3.2) + 1.5 * 0.3)";

        //35.3+2.7*22ln2.2-1.7pow/-
        //23.14pow332sqrt*3.2--1.50.3*+*

        List<string> RPN1 = ConvertToRPN(input1);
        List<string> RPN2 = ConvertToRPN(input2);

        string result1 = CalculateExpression(RPN1);
        string result2 = CalculateExpression(RPN2);

        Console.WriteLine(result1);
        Console.WriteLine(result2);
    }

    private static string CalculateExpression(List<string> RPN)
    {
        for (int i = 0; i < RPN.Count; i++)
        {
            switch (RPN[i])
            {
                case "ln":
                    RPN[i] = Math.Log(double.Parse(RPN[i - 1])).ToString();
                    RPN.RemoveAt(i - 1);
                    i--;
                    break;
                case "sqrt":
                    RPN[i] = Math.Sqrt(double.Parse(RPN[i - 1])).ToString();
                    RPN.RemoveAt(i - 1);
                    i--;
                    break;
                case "pow":
                    RPN[i] = Math.Pow(double.Parse(RPN[i - 2]), double.Parse(RPN[i - 1])).ToString();
                    RPN.RemoveAt(i - 1);
                    RPN.RemoveAt(i - 2);
                    i -= 2;
                    break;
                case "*":
                    RPN[i] = (double.Parse(RPN[i - 2]) * double.Parse(RPN[i - 1])).ToString();
                    RPN.RemoveAt(i - 1);
                    RPN.RemoveAt(i - 2);
                    i -= 2;
                    break;
                case "/":
                    RPN[i] = (double.Parse(RPN[i - 2]) / double.Parse(RPN[i - 1])).ToString();
                    RPN.RemoveAt(i - 1);
                    RPN.RemoveAt(i - 2);
                    i -= 2;
                    break;
                case "-":
                    RPN[i] = (double.Parse(RPN[i - 2]) - double.Parse(RPN[i - 1])).ToString();
                    RPN.RemoveAt(i - 1);
                    RPN.RemoveAt(i - 2);
                    i -= 2;
                    break;
                case "+":
                    RPN[i] = (double.Parse(RPN[i - 2]) + double.Parse(RPN[i - 1])).ToString();
                    RPN.RemoveAt(i - 1);
                    RPN.RemoveAt(i - 2);
                    i -= 2;
                    break;
            }
        }

        return RPN[0];
    }

    private static List<string> ConvertToRPN(string expression)
    {
        List<string> RPN = new List<string>();
        Stack<string> operators = new Stack<string>();

        expression = expression.Replace(" ", "");
        bool previousIsDigit = false;
        bool minusIsUnary = false;

        for (int i = 0; i < expression.Length; i++)
        {
            string digit = null;

            while (i < expression.Length && (char.IsDigit(expression, i) || expression[i] == '.'))
            {
                digit += expression[i];
                i++;
            }

            if (digit != null)
            {
                if (minusIsUnary)
                {
                    RPN.Add("-" + digit);
                    minusIsUnary = false;
                }
                else
                {
                    RPN.Add(digit);
                }

                i--;
                previousIsDigit = true;
            }
            else
            {
                string functionName = expression[i].ToString();

                if (!previousIsDigit && functionName == "-")
                {
                    minusIsUnary = true;
                    continue;
                }

                previousIsDigit = false;

                if (functionName == ",")
                {
                    continue;
                }

                while (!precedence.ContainsKey(functionName))
                {
                    i++;
                    functionName += expression[i];
                }

                if ((operators.Count > 0) && (precedence[functionName] < precedence[operators.Peek()]))
                {
                    RPN.Add(operators.Pop());
                    operators.Push(functionName);
                }
                else
                {
                    if (functionName == ")")
                    {
                        while (operators.Peek() != "(")
                        {
                            RPN.Add(operators.Pop());
                        }

                        operators.Pop();

                        for (int j = 0; j < functions.Length; j++)
                        {
                            if ((operators.Count > 0) && (operators.Peek() == functions[j]))
                            {
                                RPN.Add(operators.Pop());
                                break;
                            }
                        }

                        previousIsDigit = true;
                    }
                    else
                    {
                        operators.Push(functionName);
                    }
                }
            }
        }

        while (operators.Count > 0)
        {
            RPN.Add(operators.Pop());
        }

        return RPN;
    }
}
